﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.IO;
using System.Xml;

using Base;
using Base.Schema;
using Base.DataModel;

namespace XmlCompiler
{
    public static class SchemaCompiler
    {
        public static void Compile(NiftySchema schema, Stream output)
        {
            // Compile the schema to an XMLWriter
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlTextWriter.Create(output, settings);

            // Create the Schema element
            writer.WriteStartDocument();
            WriteSchemaItemElement(writer, schema);

            writer.Close();
        }

        private static void WriteSchemaItemElement(XmlWriter w, SchemaItem item)
        {
            // Write element head
            w.WriteStartElement(item.ElementName);
            if (item is IdentifiedItem)
            {
                IdentifiedItem iditem = (IdentifiedItem)item;
                w.WriteAttributeString("name", iditem.Name);
                w.WriteAttributeString("id", iditem.Id.ToString(Guid64Format.Uid));
            }

            WritePreferences(w, item);
            WriteCollections(w, item);
        }

        private static bool IsEnumerableEmpty(System.Collections.IEnumerable e)
        {
            var enumerator = e.GetEnumerator();
            bool firstValue = enumerator.MoveNext();
            enumerator.Reset();
            return !firstValue;
        }

        private static void WritePreferences(XmlWriter w, SchemaItem item)
        {
            Preference[] prefs = item.GetPreferences();
            if (prefs.Length == 0) return;

            w.WriteStartElement("preferences");
            foreach (Preference p in prefs)
            {
                // We should be careful not to write empty prefs!
                if (IsPrefEmpty(item, p)) continue;

                w.WriteStartElement("pref");
                w.WriteAttributeString("key", p.Key);

                // Handle special prefs that aren't strings
                WritePrefItemValue(w, item, p);

                w.WriteEndElement();
            }

            w.WriteEndElement();
        }

        private static void WriteCollections(XmlWriter w, SchemaItem item)
        {
            var cols = item.GetCollections();
            foreach (string name in cols.Keys)
            {
                // Don't write empty collections
                if (IsEnumerableEmpty(cols[name])) continue;

                // Write the main element
                w.WriteStartElement(name);

                foreach (object child in cols[name])
                {
                    if (child is SchemaItem)
                    {
                        WriteSchemaItemElement(w, (SchemaItem)child);
                        w.WriteEndElement();
                    }
                }
                w.WriteEndElement();
            }
        }

        private static bool IsPrefEmpty(SchemaItem item, Preference pref)
        {
            if (pref == null) return true;

            if (pref.Value is System.Collections.ICollection)
            {
                return ((System.Collections.ICollection)pref.Value).Count == 0;
            }

            return false;
        }

        private static void WritePrefItemValue(XmlWriter w, SchemaItem item, Preference pref)
        {
            if (pref.Value is Enum)
            {
                w.WriteString(pref.Value.ToString().ToLower());
            }
            else
            {
                w.WriteString(pref.Value.ToString());
            }
        }
    }
}
